/*
 * $Id: GuidanceLoader.java,v 1.6 2009/09/08 16:44:34 afaichun Exp $
 * 
 * Copyright (C) 2008 General Electric Company. All Rights Reserved.
 * 
 * This software is the confidential and proprietary information of the General
 * Electric Company (GE). You shall not disclose this software and shall use it
 * only in accordance with the terms of the license agreement you entered into
 * with GE.
 * 
 * GE MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE
 * SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR
 * NON-INFRINGEMENT. GE SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE
 * AS A RESULT OF USING, MODIFYING, OR DISTRIBUTING THIS SOFTWARE OR ITS
 * DERIVATIVES.
 */
package com.ge.healthcare.autosc.processor.shemjob.guidance.loader;

import java.io.File;
import java.util.HashMap;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import com.ge.healthcare.autosc.common.exceptions.BasicException;
import com.ge.healthcare.autosc.common.exceptions.XmlParserFactoryException;
import com.ge.healthcare.autosc.common.util.ApplicationInfo;
import com.ge.healthcare.autosc.common.util.PropertyConstants;
import com.ge.healthcare.autosc.common.util.xml.dom.DomHelper;
import com.ge.healthcare.autosc.common.util.xml.dom.IXmlParser;
import com.ge.healthcare.autosc.processor.PrcCodeConstants;
import com.ge.healthcare.autosc.processor.exception.GuidanceLoaderException;
import com.ge.healthcare.autosc.processor.shemjob.guidance.builder.IGuidanceElementBuilder;
import com.ge.healthcare.autosc.processor.xml.XmlParserFactory;

/**
 * This is the abstract class for all SHEM guidance loader. Guidance loader
 * loads the guidance file (.xml) and constructs the guidance map. Subclasses
 * shall implement their own operations to retrieve guidance data from the
 * guidance map.
 * 
 * Every guidance file should have a supporting schema file, which allow XML
 * parser to validate its conformity.
 * 
 * @author 212042946
 * 
 */
public abstract class GuidanceLoader implements IGuidanceLoader {

	protected boolean testFlag = false;

	protected Document doc = null;

	private IXmlParser xmlParser = null;

	/**
	 * Constructor
	 * 
	 * @throws GuidanceLoaderException
	 */
	public GuidanceLoader() throws GuidanceLoaderException {
		String message = "Failed initializing guidance loader";
		if (xmlParser == null) {
			try {
				xmlParser = XmlParserFactory.getXmlParser(getParserName());
				if (xmlParser == null) {
					throw new GuidanceLoaderException(message,
							PrcCodeConstants.PRC_F_SHM_010);
				}
			} catch (XmlParserFactoryException xpfe) {
				throw new GuidanceLoaderException(message, xpfe,
						PrcCodeConstants.PRC_F_SHM_001);
			}
		}
	}

	/**
	 * Build configuration map using incoming label and base node. Children of
	 * the base node will be evaluated against defined/default label to
	 * determine the key/value pair to the map
	 * 
	 * @param baseNode
	 *            Element node to look for incoming labels.
	 * @param baseDescription
	 *            For error condition.
	 * @param definedLabel
	 *            Tag name to identify interested data
	 * @param keyLabel
	 *            Child identifier under element with name
	 *            definedLabel/defaultLabel
	 * @param defaultLabel
	 *            Default tag name to identify interested data
	 * @param configLabel
	 * @param builder
	 *            Element builder that is used to build the map value
	 * @return
	 * @throws GuidanceLoaderException
	 */
	public HashMap<String, Object> buildConfigHash(Node baseNode,
			String baseDescription, String definedLabel, String keyLabel,
			String defaultLabel, String configLabel,
			IGuidanceElementBuilder builder) throws GuidanceLoaderException {
		HashMap<String, Object> myMap = new HashMap<String, Object>();
		try {
			NodeList baseChildren = baseNode.getChildNodes();
			for (int idx = 0; idx < baseChildren.getLength(); idx += 1) {
				Node currentNode = baseChildren.item(idx);
				if (currentNode.getNodeType() != Node.ELEMENT_NODE) {
					continue;
				}
				Element currentElement = (Element) currentNode;
				String currentTag = currentElement.getTagName();
				String key = null;
				if (currentTag.equals(definedLabel)) {
					Node keyNode = DomHelper.getSingleChildElement(
							currentElement, keyLabel);
					String[] keyTextList = DomHelper.getTextItemsFor(keyNode);
					key = keyTextList[0];
				} else if (currentTag.equals(defaultLabel)) {
					key = "";
				} else {
					String tagMessage = new StringBuffer().append(
							"Unexpected element ").append(currentTag).append(
							" found within ").append(baseDescription)
							.toString();
					throw new GuidanceLoaderException(tagMessage);
				}
				Node valueNode = DomHelper.getSingleChildElement(
						currentElement, configLabel);
				Object value = getConfigElemFor(valueNode, configLabel, builder);
				myMap.put(key, value);
			}
		} catch (Throwable e) {
			throw new GuidanceLoaderException("Error constructing "
					+ baseDescription, e);
		}
		return myMap;
	}

	/**
	 * Construct guidance structure from the given guidance file name
	 * 
	 * @param configFileName
	 *            Guidance file name
	 * @throws GuidanceLoaderException
	 */
	public void build(String configFileName) throws GuidanceLoaderException {

		File configFile = null;
		if (testFlag) {
			configFile = new File(new File(System.getProperty("test.home.dir"),
					"data"), configFileName);
		} else {
			configFile = new File(
					ApplicationInfo.getInstance().getProperty(
							PropertyConstants.CONFIG_FILE_DIR,
							"/autosc/runtime/env"), configFileName);
		}

		if (!configFile.exists()) {
			throw new GuidanceLoaderException("Guidance file does not exist. "
					+ configFile.getPath(), PrcCodeConstants.PRC_F_SHM_016);
		}
		try {
			doc = (Document) xmlParser.processResponse(configFile);
		} catch (Exception ioe) {
			throw new GuidanceLoaderException("Failed building Guidance", ioe, PrcCodeConstants.PRC_F_SHM_017);
		}
	}

	protected abstract String getParserName();

	/**
	 * Get the configuration element which is encoded by the specified node.
	 * 
	 * @param configNode
	 *            The node which encodes the configuration element.
	 * 
	 * @param expectedTag
	 *            Expected XML tag for the node which encodes the configuration
	 *            element.
	 * 
	 * @param builder
	 *            The builder used to build a configuration element from the
	 *            text nodes which are contained underneath a configuration
	 *            element.
	 * 
	 * @return The configuration element generated from the specified node.
	 * 
	 * @exception Exception
	 *                If an error is detected.
	 */
	private Object getConfigElemFor(Node configNode, String expectedTag,
			IGuidanceElementBuilder builder) throws GuidanceLoaderException {
		String myNodeName = configNode.getNodeName();
		Object elementValue = null;
		if (!myNodeName.equals(expectedTag)) {
			throw new GuidanceLoaderException("Unexpected element tag: "
					+ myNodeName);
		}
		try {
			elementValue = builder.buildElement(expectedTag, configNode, this);
		} catch (BasicException be) {
			throw new GuidanceLoaderException(be);
		}
		return elementValue;
	}

}
